package com.cloud.cmt.usermanage.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.angela.cloud.common.base.ResultMessage;
import com.cloud.cmt.usermanage.constant.EnterpriseConstants;
import com.cloud.cmt.usermanage.dao.EnterpriseDetailDAO;
import com.cloud.cmt.usermanage.dao.EnterpriseReferrerDAO;
import com.cloud.cmt.usermanage.dao.EnterpriseUserDAO;
import com.cloud.cmt.usermanage.dao.repository.EnterpriseDetailRepository;
import com.cloud.cmt.usermanage.dao.repository.EnterpriseReferrerRepository;
import com.cloud.cmt.usermanage.dao.repository.EnterpriseUserRepository;
import com.cloud.cmt.usermanage.entity.vo.EnterpriseDetailVO;
import com.cloud.cmt.usermanage.entity.vo.EnterpriseReferrerVO;
import com.cloud.cmt.usermanage.entity.vo.EnterpriseUserVO;
import com.cloud.cmt.usermanage.service.EnterpriseService;
import com.cloud.cmt.usermanage.service.UserManagementService;
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.logging.log4j.core.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

/**
 * 浙江卓锐科技股份有限公司 版权所有 © Copyright 2017<br>
 * 说明: <br>
 * 项目名称: cloud-maintenance <br>
 * 创建日期: 2017年10月22日 18:02 <br>
 * 作者: <a href="6492178@gmail.com">汪萌萌</a>
 */
@Service
public class EnterpriseServiceImpl implements EnterpriseService {

    private static Logger LOGGER = LoggerFactory.getLogger(EnterpriseServiceImpl.class);
    /**
     * The Build.
     */
    private LoadingCache<Object, Integer> build = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(SMS_TIME_OUT, TimeUnit.MINUTES)
            .initialCapacity(100)
            .build(CacheLoader.from(() -> null));


    /**
     * The constant SMS_IP_LIMIT.
     */
    public static final int SMS_IP_LIMIT = 5;
    /**
     * The constant SMS_PHONE_LIMIT.
     */
    public static final int SMS_PHONE_LIMIT = 3;
    /**
     * The constant SMS_VERIFICATION_CODE_TEMPID.
     * 验证码短信模板
     */
    public static final String SMS_VERIFICATION_CODE_TEMPID = "SMS_109395297";
    /**
     * The constant SMS_TIME_OUT.
     * 验证码有效时间
     */
    public static final int SMS_TIME_OUT = 2;

    /**
     * The constant HOTEL_TYPE_PREFIX.酒店前缀
     */
    public static final String HOTEL_TYPE_PREFIX = "JD_";
    /**
     * The constant PROPERTY_TYPE_PREFIX.物业前缀
     */

    public static final String PROPERTY_TYPE_PREFIX = "WY_";

    /**
     * The Reentrant lock.
     */
    ReentrantLock reentrantLock = new ReentrantLock();
    private static volatile long counts = -1;

    @Value("${qiniu.accessKey}")
    private String accessKey;
    @Value("${qiniu.secretKey}")
    private String secretKey;
    @Value("${qiniu.bucket}")
    private String bucket;
    @Value("${qiniu.defaultHost}")
    private String defaultHost;
    @Value("${ali.sms.accessKeyId}")
    private String accessKeyId;
    @Value("${ali.sms.accessKeySecret}")
    private String accessKeySecret;
    @Resource
    private EnterpriseDetailRepository detailRepository;
    @Resource
    private EnterpriseUserRepository userRepository;
    @Resource
    private EnterpriseReferrerRepository referrerRepository;
    @Resource
    private UserManagementService userManagementService;
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private Map<String, List<String>> smsIpMap = new ConcurrentHashMap<>();
    private Map<String, Integer> smsPhoneMap = new ConcurrentHashMap<>();


    @Override
    public ResultMessage checkUsername(String username) {
        long count = userRepository.countByUsername(username);
        if (count > 0) {
            return ResultMessage.defaultError("用户名已存在");
        }
        return ResultMessage.success("用户名可用");
    }

    @Override
    public ResultMessage checkSocialCreditCode(String socialCreditCode) {
        long count = detailRepository.countBySocialCreditCode(socialCreditCode);
        if (count > 0) {
            return ResultMessage.defaultError("该营业执照已注册");
        }
        return ResultMessage.success("该营业执照未注册");
    }

    @Override
    public ResultMessage upLoadPic(MultipartFile file) {
        Configuration cfg = new Configuration(Zone.zone0());
        UploadManager uploadManager = new UploadManager(cfg);
        Auth auth = Auth.create(accessKey, secretKey);
        String uploadToken = auth.uploadToken(bucket);
        String key = file.getOriginalFilename();
        try {
            Response response = null;
            try {
                response = uploadManager.put(file.getBytes(), key, uploadToken);
            } catch (IOException e) {
                e.printStackTrace();
            }
            DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
            return ResultMessage.success("上传成功", defaultHost + putRet.key);
        } catch (QiniuException e) {
            e.printStackTrace();
            return ResultMessage.defaultError("上传失败");
        }
    }

    @Override
    public ResultMessage getSmsVerificationCode(HttpServletRequest request, String phone) {
        reentrantLock.lock();
        try {
            //<editor-fold desc="校验手机号码是否合法">
            String phoneReg = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$";
            boolean matches = Pattern.compile(phoneReg).matcher(phone).matches();
            if (!matches) {
                return ResultMessage.defaultError("请填写正确的手机号码");
            }
            //</editor-fold>
            //<editor-fold desc="校验同一手机号码发送次数是否超过三次">
            String sessionId = request.getSession().getId();
            Integer smsPhoneCounts = smsPhoneMap.get(phone);
            if (smsPhoneCounts != null && smsPhoneCounts >= SMS_PHONE_LIMIT) {
                return ResultMessage.defaultError("同一手机号一天只能发三次短信");
            }
            //</editor-fold>
            //<editor-fold desc="检查距离上次发送是否超过六十秒">
            if (build.getIfPresent(phone) != null) {
                return ResultMessage.defaultError("同一手机号每次申请间隔不低于120s");
            }
            //</editor-fold>
            //<editor-fold desc="检查同一电脑一天是否发送超过五个号码">
            List<String> list = smsIpMap.get(sessionId);
            if (list == null) {
                list = new ArrayList<>();
                list.add(phone);
            } else {
                if (!list.contains(phone)) {
                    list.add(phone);
                }
                if (list.size() >= SMS_IP_LIMIT) {
                    return ResultMessage.defaultError("一台电脑一天只能注册5个手机号");
                }
            }
            //</editor-fold>
            //<editor-fold desc="发送短信">
            smsIpMap.put(sessionId, list);
            smsPhoneCounts = smsPhoneCounts == null ? 0 : smsPhoneCounts;
            smsPhoneMap.put(phone, ++smsPhoneCounts);
            int code = ThreadLocalRandom.current().nextInt(99999, 999999);
            LOGGER.debug("phone:{}第{}次注册验证码为:{}", phone, smsPhoneCounts, code);
            build.put(phone, code);
            JSONObject param = new JSONObject();
            param.put("code", code);
            threadPoolTaskExecutor.submit(() -> sendSms(phone, accessKeyId, accessKeySecret, SMS_VERIFICATION_CODE_TEMPID, param));
            //</editor-fold>
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
        return ResultMessage.success("获取成功");

    }

    private ResultMessage checkSmsVerificationCode(String phone, Integer code) {
        Integer integer = build.getIfPresent(phone);
        build.invalidate(phone);
        if (code.equals(integer)) {
            return ResultMessage.success("校验成功");
        }
        return ResultMessage.defaultError("短信验证码有误");

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMessage applyForProbation(EnterpriseDetailVO enterpriseDetailVO, EnterpriseUserVO enterpriseUserVO, EnterpriseReferrerVO enterpriseReferrerVO, Integer code) {
        ResultMessage resultMessage;
        try {
            reentrantLock.lock();
            //<editor-fold desc="校验短信验证码">
            String phone = enterpriseDetailVO.getPhone();
            resultMessage = this.checkSmsVerificationCode(phone, code);
            //</editor-fold>
            if (!resultMessage.getIsSuccess()) {
                return resultMessage;
            }
            //<editor-fold desc="校验企业是否已注册">
            String socialCreditCode = enterpriseDetailVO.getSocialCreditCode();
            resultMessage = this.checkSocialCreditCode(socialCreditCode);
            if (!resultMessage.getIsSuccess()) {
                return resultMessage;
            }
            //</editor-fold>

            //<editor-fold desc="校验用户名是否存在">
            String username = enterpriseUserVO.getUsername();
            resultMessage = this.checkUsername(username);

            if (!resultMessage.getIsSuccess()) {
                return resultMessage;
            }

            //</editor-fold>
            //<editor-fold desc="设置用户编号">
            EnterpriseDetailDAO enterpriseDetailDAO = setUserCode(enterpriseDetailVO);

            //</editor-fold>
            //<editor-fold desc="保存详情">
            enterpriseDetailDAO = detailRepository.save(enterpriseDetailDAO);
            //</editor-fold>
            //<editor-fold desc="保存用户信息">
            Long enterpriseId = enterpriseDetailDAO.getId();
            //<editor-fold desc="MD5加密">
            byte[] bytes = enterpriseUserVO.getPassword().getBytes();
            String password = Md5Crypt.md5Crypt(bytes);
            //</editor-fold>
            String uuid = UuidUtil.getTimeBasedUuid().toString();
            EnterpriseUserDAO enterpriseUserDAO = EnterpriseUserDAO.builder()
                    .accountStatus(EnterpriseConstants.ACCOUNT_STATUS_PENDING_AUDIT.getValue())
                    .enterpriseId(enterpriseId)
                    .enable(true)
                    .serialNumber(uuid).build();

            enterpriseUserVO.setPassword(password);
            BeanUtils.copyProperties(enterpriseUserVO, enterpriseUserDAO);
            userRepository.save(enterpriseUserDAO);
            //</editor-fold>
            //<editor-fold desc="保存推荐人关系">
            if (enterpriseReferrerVO != null && !Strings.isNullOrEmpty(enterpriseReferrerVO.getReferrer())) {
                EnterpriseReferrerDAO enterpriseReferrerDAO = new EnterpriseReferrerDAO();
                BeanUtils.copyProperties(enterpriseReferrerVO, enterpriseReferrerDAO);
                enterpriseReferrerDAO.setEnterpriseId(enterpriseId);
                referrerRepository.save(enterpriseReferrerDAO);
            }
            //</editor-fold>

        } catch (BeansException e) {
            counts--;
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }

        return ResultMessage.success("申请成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMessage reApplyForProbation(EnterpriseDetailVO enterpriseDetailVO) {
        try {
            reentrantLock.lock();

            Long enterpriseDetailVOId = enterpriseDetailVO.getId();
            ResultMessage<EnterpriseUserDAO> resultMessage = userManagementService.checkAccountStatusIsAllowToChange(enterpriseDetailVOId, EnterpriseConstants.ACCOUNT_STATUS_PENDING_AUDIT);
            if (!resultMessage.getIsSuccess()) {
                return resultMessage;
            }
            //<editor-fold desc="保存详情">
            EnterpriseDetailDAO enterpriseDetailDAO = new EnterpriseDetailDAO();
            BeanUtils.copyProperties(enterpriseDetailVO, enterpriseDetailDAO, "version", "socialCreditCode");
            detailRepository.save(enterpriseDetailDAO);
            //</editor-fold>

            //<editor-fold desc="变更账户状态">
            EnterpriseUserDAO enterpriseUserDAO = resultMessage.getData();
            userRepository.save(enterpriseUserDAO);
            //</editor-fold>


        } catch (BeansException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }

        return ResultMessage.success("申请成功");
    }

    /**
     * 设置用户编号,根据行业类型加前缀,并根据当前时间和该条数据的序号拼接
     *
     * @param enterpriseDetailVO
     * @return
     */
    private EnterpriseDetailDAO setUserCode(EnterpriseDetailVO enterpriseDetailVO) {
        StringBuilder prefix = new StringBuilder();
        int industry = enterpriseDetailVO.getIndustry();
        if (EnterpriseConstants.INDUSTRY_TYPE_HOTEL.getValue() == industry) {
            prefix.append(HOTEL_TYPE_PREFIX);
        } else if (EnterpriseConstants.INDUSTRY_TYPE_PROPERTY.getValue() == industry) {
            prefix.append(PROPERTY_TYPE_PREFIX);
        }
        prefix.append(LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE));
        if (counts == -1) {
            counts = detailRepository.count();
        }
        prefix.append(String.format("%05d", ++counts));
        EnterpriseDetailDAO enterpriseDetailDAO = new EnterpriseDetailDAO();
        BeanUtils.copyProperties(enterpriseDetailVO, enterpriseDetailDAO, "version");
        enterpriseDetailDAO.setUserCode(prefix.toString());
        return enterpriseDetailDAO;
    }


    private void sendSms(String phone, String accessKeyId, String accessKeySecret, String templateCode, JSONObject param) {
        //设置超时时间-可自行调整
        //初始化ascClient需要的几个参数
        final String product = "Dysmsapi";//短信API产品名称（短信产品名固定，无需修改）
        final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名（接口地址固定，无需修改）
        //初始化ascClient,暂时不支持多region（请勿修改）
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,
                accessKeySecret);
        try {
            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        } catch (ClientException e) {
            e.printStackTrace();
        }
        IAcsClient acsClient = new DefaultAcsClient(profile);
        //组装请求对象
        SendSmsRequest request = new SendSmsRequest();
        //使用post提交
        request.setMethod(MethodType.POST);
        //必填:待发送手机号。支持以逗号分隔的形式进行批量调用，批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式
        request.setPhoneNumbers(phone);
        //必填:短信签名-可在短信控制台中找到
        request.setSignName("果咖网");
        //必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templateCode);
        //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        //友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败

        request.setTemplateParam(param.toJSONString());
        //请求失败这里会抛ClientException异常
        SendSmsResponse sendSmsResponse = null;
        try {
            long s = System.currentTimeMillis();
            sendSmsResponse = acsClient.getAcsResponse(request);
            long e = System.currentTimeMillis();
            LOGGER.debug("消耗时间:" + (e - s));
        } catch (ClientException e) {
            e.printStackTrace();
        }
        if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
            //请求成功
            LOGGER.debug("发送短信响应信息{}", JSON.toJSONString(sendSmsResponse));
        }
    }

}
